package com.franklin.ideaplugin.api.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author Ye Junhui
 * @since 2023/5/15
 */
public class ReflectionUtils {

    /**
     * 找到不是Object的所有父类
     * @param clazz
     * @return
     */
    public static List<Class<?>> getClassAllSuperClasses(Class<?> clazz){
        List<Class<?>> list = new ArrayList<>();
        Class<?> tempClazz = clazz;
        while (Objects.nonNull(tempClazz.getSuperclass()) && !tempClazz.getSuperclass().equals(Object.class)){
            tempClazz = tempClazz.getSuperclass();
            list.add(tempClazz);
        }
        return list;
    }

    /**
     * 获取类实现的所有接口
     * @param clazz
     * @return
     */
    public static List<Class<?>> doGetClassAllInterfaces(Class<?> clazz){
        Set<Class<?>> classSet = new LinkedHashSet<>();
        List<Class<?>> classAllSuperClasses = getClassAllSuperClasses(clazz);
        classAllSuperClasses.add(clazz);
        for (Class<?> clz : classAllSuperClasses) {
            doGetClassAllInterfaces(clz,classSet);
        }
        return new ArrayList<>(classSet);
    }

    /**
     * 递归
     * @param clazz
     * @param classSet
     */
    private static void doGetClassAllInterfaces(Class<?> clazz, Set<Class<?>> classSet){
        for (Class<?> clazzInterface : clazz.getInterfaces()) {
            classSet.add(clazzInterface);
            doGetClassAllInterfaces(clazzInterface,classSet);
        }
    }

    /**
     * 获取类实现的所有带泛型接口
     * @param clazz
     * @return
     */
    public static List<ParameterizedType> getClassAllGenericInterfaces(Class<?> clazz){
        Set<ParameterizedType> classSet = new LinkedHashSet<>();
        List<Class<?>> classAllSuperClasses = getClassAllSuperClasses(clazz);
        classAllSuperClasses.add(clazz);
        for (Class<?> clz : classAllSuperClasses) {
            doGetClassAllGenericInterfaces(clz,classSet);
        }
        return new ArrayList<>(classSet);
    }

    /**
     * 递归
     * @param clazz
     * @param classSet
     */
    private static void doGetClassAllGenericInterfaces(Class<?> clazz,Set<ParameterizedType> classSet){
        for (Type type : clazz.getGenericInterfaces()) {
            if (type instanceof ParameterizedType){
                ParameterizedType parameterizedType = (ParameterizedType) type;
                classSet.add(parameterizedType);
                doGetClassAllGenericInterfaces((Class<?>) parameterizedType.getRawType(),classSet);
            }
        }
    }

    /**
     * 获取父接口的泛型
     * @param clazz
     * @param interfaceIndex
     * @param index
     * @return
     */
    public static Class<?> getSuperInterfaceGenericType(final Class<?> clazz, final int interfaceIndex, final int index) {
        Type[] genericInterfaces = clazz.getGenericInterfaces();
        if (ArrayUtil.isEmpty(genericInterfaces)){
            return Object.class;
        }
        if (interfaceIndex >= genericInterfaces.length || interfaceIndex < 0){
            return Object.class;
        }
        Type genType = genericInterfaces[interfaceIndex];
        if (!(genType instanceof ParameterizedType)) {
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            return Object.class;
        }
        return (Class<?>) params[index];
    }

    /**
     * 获取父接口的泛型
     * @param clazz
     * @param interfaceClazz
     * @param index
     * @return
     */
    public static Class<?> getSuperInterfaceGenericType(Class<?> clazz, final Class<?> interfaceClazz, final int index) {
        List<ParameterizedType> interfaces = getClassAllGenericInterfaces(clazz);
        if (CollectionUtil.isEmpty(interfaces)){
            return Object.class;
        }
        for (ParameterizedType anInterface : interfaces) {
            if (anInterface.getRawType().equals(interfaceClazz)) {
                Type[] params = anInterface.getActualTypeArguments();
                if (index >= params.length || index < 0) {
                    return Object.class;
                }
                if (!(params[index] instanceof Class)) {
                    return Object.class;
                }
                return (Class<?>) params[index];
            }
        }
        return Object.class;
    }

    /**
     * <p>
     * 反射对象获取泛型
     * </p>
     *
     * @param clazz 对象
     * @param index 泛型所在位置
     * @return Class
     */
    public static Class<?> getSuperClassGenericType(final Class<?> clazz, final int index) {
        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType)) {
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            return Object.class;
        }
        return (Class<?>) params[index];
    }

    /**
     * 是否是数组
     * @param classQualifiedName
     * @return
     */
    public static boolean isArrayClass(String classQualifiedName){
        return classQualifiedName.endsWith("[]") || classQualifiedName.endsWith("...");
    }

    /**
     * 获取数组的类
     * @param classQualifiedName
     * @return
     * @throws ClassNotFoundException
     */
    public static Class<?> getArrayClass(String classQualifiedName) throws ClassNotFoundException {
        if (!isArrayClass(classQualifiedName)){
            throw new IllegalArgumentException(classQualifiedName + " is not a array class");
        }
        if (classQualifiedName.contains("...")){
            classQualifiedName = classQualifiedName.replace("...","[]");
        }
        int count = 0;
        String tempName = classQualifiedName;
        while (tempName.contains("[]")){
            count++;
            tempName = tempName.replaceFirst("\\[]","");
        }
        String prefix = StrUtil.repeat('[', count);
        String arrClassName = prefix + "L" + tempName + ";";
        return ClassUtil.loadClass(arrClassName);
    }

    /**
     * 获取数组的维数
     * @param classQualifiedName
     * @return
     */
    public static int getArrayDimension(String classQualifiedName){
        if (!isArrayClass(classQualifiedName)){
            throw new IllegalArgumentException(classQualifiedName + " is not a array class");
        }
        if (classQualifiedName.contains("...")){
            classQualifiedName = classQualifiedName.replace("...","[]");
        }
        int count = 0;
        String tempName = classQualifiedName;
        while (tempName.contains("[]")){
            count++;
            tempName = tempName.replaceFirst("\\[]","");
        }
        return count;
    }

    /**
     * 是否是java基本数据类型
     * @param className
     * @return
     */
    public static boolean isJavaBasicType(String className){
        if (StrUtil.isBlank(className)){
            return false;
        }
        if (className.equals("byte")){
            return true;
        }
        if (className.equals("short")){
            return true;
        }
        if (className.equals("int")){
            return true;
        }
        if (className.equals("long")){
            return true;
        }
        if (className.equals("boolean")){
            return true;
        }
        if (className.equals("double")){
            return true;
        }
        if (className.equals("float")){
            return true;
        }
        if (className.equals("char")){
            return true;
        }
        return false;
    }

    /**
     * 获取基础数据类型
     * @param className
     * @return
     */
    public static Class<?> getJavaBasicClass(String className){
        if (StrUtil.isBlank(className)){
            return null;
        }
        if (className.equals("byte")){
            return byte.class;
        }
        if (className.equals("short")){
            return short.class;
        }
        if (className.equals("int")){
            return int.class;
        }
        if (className.equals("long")){
            return long.class;
        }
        if (className.equals("boolean")){
            return boolean.class;
        }
        if (className.equals("double")){
            return double.class;
        }
        if (className.equals("float")){
            return float.class;
        }
        if (className.equals("char")){
            return char.class;
        }
        return null;
    }

    /**
     * 获取基础数据类型的默认值
     * @param className
     * @return
     */
    public static Object getJavaBasicTypeDefaultValue(String className){
        if (StrUtil.isBlank(className)){
            return null;
        }
        if (className.equals("byte")){
            return 0;
        }
        if (className.equals("short")){
            return 0;
        }
        if (className.equals("int")){
            return 0;
        }
        if (className.equals("long")){
            return 0L;
        }
        if (className.equals("boolean")){
            return false;
        }
        if (className.equals("double")){
            return 0.0d;
        }
        if (className.equals("float")){
            return 0f;
        }
        if (className.equals("char")){
            return 'a';
        }
        return null;
    }

    /**
     * 判断是否是基本数据类型
     * @param clazz
     * @return
     */
    public static boolean isBasicType(Class<?> clazz) {
        if (Objects.isNull(clazz)){
            return false;
        }
        String canonicalName = clazz.getCanonicalName();
        if (isJavaBasicType(canonicalName)){
            return true;
        }
        if (
                "java.lang.String".equals(canonicalName)
                        ||
                        "java.lang.CharSequence".equals(canonicalName)
        ){
            return true;
        }
        if (
                "java.lang.Integer".equals(canonicalName)
                        ||
                        "int".equals(canonicalName)
        ){
            return true;
        }
        if (
                "java.lang.Double".equals(canonicalName)
                        ||
                        "double".equals(canonicalName)
        ){
            return true;
        }
        if (
                "java.lang.Float".equals(canonicalName)
                        ||
                        "float".equals(canonicalName)
        ){
            return true;
        }
        if (
                "java.lang.Short".equals(canonicalName)
                        ||
                        "short".equals(canonicalName)
        ){
            return true;
        }
        if (
                "java.lang.Long".equals(canonicalName)
                        ||
                        "long".equals(canonicalName)
        ){
            return true;
        }
        if (
                "java.lang.Character".equals(canonicalName)
                        ||
                        "char".equals(canonicalName)
        ){
            return true;
        }
        if ("java.math.BigDecimal".equals(canonicalName)){
            return true;
        }
        if (
                "java.lang.Boolean".equals(canonicalName)
                        ||
                        "boolean".equals(canonicalName)
        ){
            return true;
        }
        return false;
    }

    /**
     * 是否是文件类型
     *
     * @param classQualifiedName
     * @return
     */
    public static boolean isFileType(String classQualifiedName) {
        if (classQualifiedName.equals("org.springframework.web.multipart.MultipartFile")) {
            return true;
        }
        if (classQualifiedName.equals("org.springframework.http.codec.multipart.FilePart")) {
            return true;
        }
        return false;
    }

    /**
     * 是否是日期类型
     *
     * @param classQualifiedName
     * @return
     */
    public static boolean isDateType(String classQualifiedName) {
        if (classQualifiedName.equals("java.util.Date")) {
            return true;
        }
        if (classQualifiedName.equals("java.time.LocalDateTime")) {
            return true;
        }
        if (classQualifiedName.equals("java.time.LocalDate")) {
            return true;
        }
        return false;
    }
}
